import { app } from 'electron';
import fs from 'fs';
import os from 'os';
import path from 'path';
import { EventEmitter } from 'events';
import type { AIFormValues } from '@/packages/ai/config';
import type { CustomTheme } from '@/apps/web-app/store/theme-store';
import { getSpaceRegistry } from '../space-registry';

export interface GraftConfig {
    // URL for the Graft MetaStore
    metastore: string;
    // URL for the Graft PageStore
    pagestore: string;
    // API token for MetaStore and PageStore (optional)
    token?: string;
    // Enable/disable background synchronization
    autosync: boolean;
    // Unique Client ID (optional, auto-generated if not set)
    clientId?: string;
}

export interface AppConfig {
    // the folder where the data is stored (deprecated, use space registry instead)
    dataFolder?: string;
    // the api agent config
    apiAgentConfig: {
        url: string;
        enabled: boolean;
    };
    ai: AIFormValues;
    // Security configuration
    security: {
        webSecurity: boolean;
        crossOriginDomains: string[];
    };
    // Sync configuration
    sync: {
        enabled: boolean;
        graft: GraftConfig;
        // Future sync-related settings can go here
    };
    // Auto-update configuration
    autoUpdate: {
        enabled: boolean;
    };
    theme: {
        currentThemeName: string;
        customThemes: CustomTheme[];
    };
    // Last opened workspace ID
    lastOpenedSpace?: string;
}

const emptyConfig: AppConfig = {
    dataFolder: undefined, // Deprecated, use space registry instead
    apiAgentConfig: {
        url: '',
        enabled: false,
    },
    ai: {
        localModels: [],
        llmProviders: [],
        autoLoadEmbeddingModel: false,
        embeddingModel: '',
        translationModel: '',
        codingModel: '',
        version: 0,
    },
    security: {
        webSecurity: true,
        crossOriginDomains: [],
    },
    sync: { // Updated structure
        enabled: false,
        graft: {
            metastore: 'http://127.0.0.1:3001', // Default from docs
            pagestore: 'http://127.0.0.1:3000', // Default from docs
            token: undefined, // No default token
            autosync: true, // Default from docs
            clientId: undefined, // Will be auto-generated by graft if not set
        },
    },
    autoUpdate: {
        enabled: true,
    },
    theme: {
        currentThemeName: 'Default',
        customThemes: [],
    },
    lastOpenedSpace: undefined,
};

export class ConfigManager extends EventEmitter {
    private configPath: string;
    private config: AppConfig;
    private isGlobalConfig: boolean;

    constructor(configPath: string, isGlobalConfig: boolean = false) {
        super();
        this.configPath = configPath;
        this.isGlobalConfig = isGlobalConfig;
        this.config = this.loadConfig();
        // Ensure existing configs have the new sync structure and defaults
        this.ensureDefaultSyncConfig();
        // Ensure AI config has version field for synchronization
        this.ensureDefaultAIConfig();
    }

    private loadConfig(): AppConfig {
        let loadedConfig = JSON.parse(JSON.stringify(emptyConfig)); // Deep clone defaults
        if (fs.existsSync(this.configPath)) {
            try {
                const rawData = fs.readFileSync(this.configPath, 'utf-8');
                const parsedData = JSON.parse(rawData);

                // Simple migration: If old top-level 'graft' exists and 'sync' doesn't, move it.
                if (parsedData.graft && !parsedData.sync) {
                    console.warn("Migrating top-level 'graft' config to 'sync.graft'.");
                    parsedData.sync = { graft: parsedData.graft };
                    // We don't know the old intent for 'enabled', so default it.
                    // The ensureDefaultSyncConfig will handle setting it if missing.
                    delete parsedData.graft; // Remove old top-level key
                }

                // Deep merge ensuring 'sync' and 'sync.graft' exist
                // We need to handle the merge carefully to include the new 'enabled' field
                loadedConfig = {
                    ...loadedConfig, // Start with defaults
                    ...parsedData,   // Overlay saved config
                    sync: {          // Merge the sync object explicitly
                        ...(loadedConfig.sync || {}), // Start with default sync object structure
                        ...(parsedData.sync || {}),   // Overlay saved sync settings (potentially including 'enabled')
                        graft: {                      // Ensure graft structure is merged
                            ...(loadedConfig.sync?.graft || {}), // Start with default graft settings
                            ...((parsedData.sync && parsedData.sync.graft) || {}), // Overlay saved graft settings
                        },
                    },
                };
            } catch (error) {
                console.error("Error loading or parsing config file:", this.configPath, error);
                // Fallback to deep clone of default config if loading/parsing fails
                loadedConfig = JSON.parse(JSON.stringify(emptyConfig));
            }
        }
        return loadedConfig;
    }

    // Ensure that the loaded AI config has version field for synchronization
    private ensureDefaultAIConfig(): void {
        let needsSave = false;
        // Ensure ai object exists
        if (typeof this.config.ai !== 'object' || this.config.ai === null) {
            console.warn("AI config section missing, initializing with defaults.");
            this.config.ai = JSON.parse(JSON.stringify(emptyConfig.ai)); // Deep clone default AI
            needsSave = true;
        } else {
            // Ensure version field exists
            if (typeof this.config.ai.version !== 'number') {
                console.warn("AI config version missing or invalid, applying default.");
                this.config.ai.version = emptyConfig.ai.version;
                needsSave = true;
            }
        }
        if (needsSave) {
            this.saveConfig();
        }
    }

    // Ensure that the loaded config has the sync structure, applying defaults if missing
    // Renamed from ensureDefaultSyncGraftConfig to handle sync.enabled as well
    private ensureDefaultSyncConfig(): void {
        let needsSave = false;
        // Ensure sync object exists
        if (typeof this.config.sync !== 'object' || this.config.sync === null) {
            console.warn("Sync config section missing, initializing with defaults.");
            this.config.sync = JSON.parse(JSON.stringify(emptyConfig.sync)); // Deep clone default sync
            needsSave = true;
        } else {
            // Check if sync.enabled is missing and apply default
            if (typeof this.config.sync.enabled !== 'boolean') {
                console.warn(`Sync config key 'enabled' missing or invalid, applying default.`);
                this.config.sync.enabled = emptyConfig.sync.enabled;
                needsSave = true;
            }

            // Ensure sync.graft object exists
            if (typeof this.config.sync.graft !== 'object' || this.config.sync.graft === null) {
                console.warn("Sync.graft config section missing, initializing with defaults.");
                this.config.sync.graft = { ...emptyConfig.sync.graft };
                needsSave = true;
            } else {
                // Check individual keys within sync.graft and apply defaults if missing
                for (const key of Object.keys(emptyConfig.sync.graft) as Array<keyof GraftConfig>) {
                    // Ensure the key exists and is of the correct type (basic check)
                    // If graft exists, we assume its internal keys might be missing or wrong type
                    if (!(key in this.config.sync.graft) || typeof (this.config.sync.graft as any)[key] !== typeof emptyConfig.sync.graft[key]) {
                        // Apply default only if key is missing, handle type mismatch potentially elsewhere if needed
                        if (!(key in this.config.sync.graft)) {
                            console.warn(`Sync.graft config key '${key}' missing, applying default.`);
                            (this.config.sync.graft as any)[key] = emptyConfig.sync.graft[key];
                            needsSave = true;
                        } else if (key === 'token' && typeof this.config.sync.graft.token === 'undefined') {
                            // Special case: allow undefined for token
                        }
                        else if (typeof (this.config.sync.graft as any)[key] !== typeof emptyConfig.sync.graft[key]) {
                            console.warn(`Sync.graft config key '${key}' has incorrect type, applying default.`);
                            (this.config.sync.graft as any)[key] = emptyConfig.sync.graft[key];
                            needsSave = true;
                        }
                    }
                }
            }
        }
        if (needsSave) {
            this.saveConfig();
        }
    }

    private saveConfig(): void {
        try {
            const rawData = JSON.stringify(this.config, null, 2);
            // Ensure directory exists before writing
            fs.mkdirSync(path.dirname(this.configPath), { recursive: true });
            fs.writeFileSync(this.configPath, rawData, 'utf-8');
        } catch (error) {
            console.error("Error saving config file:", this.configPath, error);
        }
    }

    // Type-safe getter for top-level keys
    public get<K extends keyof AppConfig>(key: K): AppConfig[K] {
        return this.config[key];
    }

    // Type-safe setter for top-level keys
    public set<K extends keyof AppConfig>(key: K, value: AppConfig[K]): void {
        const oldValue = this.config[key];
        if (JSON.stringify(oldValue) !== JSON.stringify(value)) { // Avoid saving if no change
            this.config[key] = value;
            this.saveConfig();
            console.log('Config changed:', { key, oldValue, newValue: value });
            this.emit('configChanged', { key, oldValue, newValue: value });
        }
    }

    // Getter for graft settings
    public getGraftConfig(): GraftConfig {
        // Return a copy to prevent direct modification
        // Ensure sync and graft exist before accessing, falling back to defaults if necessary
        const graftConfig = this.config.sync?.graft;
        return { ...(graftConfig || emptyConfig.sync.graft) };
    }

    // Setter for graft settings (replaces the entire graft object or merges partial)
    public setGraftConfig(graftConfig: Partial<GraftConfig>): void {
        // Ensure sync object exists
        if (!this.config.sync) {
            this.config.sync = JSON.parse(JSON.stringify(emptyConfig.sync));
        }
        const oldGraftConfig = { ...(this.config.sync.graft || emptyConfig.sync.graft) };
        const newGraftConfig = { ...oldGraftConfig, ...graftConfig }; // Merge partial updates

        if (JSON.stringify(oldGraftConfig) !== JSON.stringify(newGraftConfig)) {
            this.config.sync.graft = newGraftConfig;
            this.saveConfig();
            console.log('Graft config changed (within sync):', { oldValue: oldGraftConfig, newValue: newGraftConfig });
            // Emit a specific event or use the generic one, adjusting payload structure
            this.emit('configChanged', { key: 'sync.graft', oldValue: oldGraftConfig, newValue: newGraftConfig });
        }
    }

    // Getter for sync enabled status
    public isSyncEnabled(): boolean {
        // Ensure sync exists before accessing, falling back to default if necessary
        return this.config.sync?.enabled ?? emptyConfig.sync.enabled;
    }

    // Setter for sync enabled status
    public setSyncEnabled(enabled: boolean): void {
        // Ensure sync object exists
        if (!this.config.sync) {
            this.config.sync = JSON.parse(JSON.stringify(emptyConfig.sync));
        }
        const oldValue = this.config.sync.enabled;
        if (oldValue !== enabled) {
            this.config.sync.enabled = enabled;
            this.saveConfig();
            console.log('Sync enabled status changed:', { oldValue, newValue: enabled });
            this.emit('configChanged', { key: 'sync.enabled', oldValue, newValue: enabled });
        }
    }

    // Getter for auto-update enabled status
    public isAutoUpdateEnabled(): boolean {
        return this.config.autoUpdate?.enabled ?? emptyConfig.autoUpdate.enabled;
    }

    // Setter for auto-update enabled status
    public setAutoUpdateEnabled(enabled: boolean): void {
        const oldValue = this.config.autoUpdate.enabled;
        if (oldValue !== enabled) {
            this.config.autoUpdate.enabled = enabled;
            this.saveConfig();
            console.log('Auto-update enabled status changed:', { oldValue, newValue: enabled });
            this.emit('configChanged', { key: 'autoUpdate.enabled', oldValue, newValue: enabled });
        }
    }

    // Getter for last opened space
    public getLastOpenedSpace(): string | undefined {
        return this.config.lastOpenedSpace;
    }

    // Setter for last opened space
    public setLastOpenedSpace(spaceId: string | undefined): void {
        const oldValue = this.config.lastOpenedSpace;
        if (oldValue !== spaceId) {
            this.config.lastOpenedSpace = spaceId;
            this.saveConfig();
            console.log('Last opened space changed:', { oldValue, newValue: spaceId });
            this.emit('configChanged', { key: 'lastOpenedSpace', oldValue, newValue: spaceId });
        }
    }
}

let configManagerInstance: ConfigManager | null = null;

export function getConfigManager(): ConfigManager {
    if (!configManagerInstance) {
        const globalConfigPath = path.join(os.homedir(), '.eidos', 'config.json');
        if (fs.existsSync(globalConfigPath)) {
            configManagerInstance = new ConfigManager(globalConfigPath, true);
        } else {
            const userDataPath = app.getPath('userData');
            const configFilePath = path.join(userDataPath, 'config.json');
            configManagerInstance = new ConfigManager(configFilePath, false);
        }
    }
    return configManagerInstance;
}
